/*
 * Copyright (c) 2007 centrevillage <centrevillage@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <iostream>
#include <string.h>

#include "wavedef.h"
#include "waveutil.h"
#include "VstPlugin.h"
#include "WaveOutDevice.h"
#include "VstHost.h"

#include <seq/MidiMsg.h>
#include <seq/Sequence.h>
#include <seq/MidiMsgList.h>
#include <seq/Project.h>

#include <boost/shared_ptr.hpp>

using namespace wave;
using namespace boost;
using namespace std;
using namespace seq;

int main(int argc, const char* argv[]) {
    const char* fname = "scene1.mid";
    const char* pname = "Synth1 VST.dll";
    int flag = 0;
    if (argc > 1) fname = argv[1];
    if (argc > 2) pname = argv[2];
    if (argc > 3) {
        if (strncmp("test", argv[3], 4) == 0) {
            flag = 1;
        } else if (strncmp("miku", argv[3], 4) == 0) {
            flag = 2;
            pname = "I:\\SoftWare\\VSTplugins\\VOCALOID2.dll";
        }
    }
    WaveFmt fmt;
    VstPlugin plugin;
    plugin.load(pname);
    plugin.open(fmt.getSamplesPerSec(), 65536*4);
    VstHost host;
    host.setBufSize(65536*4);
    host.reset();
    int id = host.put(plugin, VstHost::START);
    cout << plugin.toString();
    if (flag == 1) {
        // test
        seq::MidiMsg msgon(NOTE_ON/*code*/, 1/*channel*/, 64/*data1*/, 127/*data2*/);
//        seq::MidiMsg msgoff(NOTE_OFF/*code*/, 1/*channel*/, 0/*data1*/, 0/*data2*/);
        MidiMsgList msgList;
        msgList.add(0, msgon, 480);
//        msgList.add(960, msgoff);
        host.setMidiMsgs((size_t)id, msgList);
        const RVstEvents& events = host.getPlugin((size_t)id).getEvents();
        cout << "[events] start" << endl;
        cout << events.toString() << endl;
        cout << "[events] end" << endl;
    } else if (flag == 2) {
        // miku
        MidiMsgList msgList;
#define BUILD_NRPN(CHANNEL_, TICK_, MSB_, LSB_, DMSB_, DLSB_) \
        msgList.add(TICK_, seq::MidiMsg(CONTROL, CHANNEL_, 99, MSB_), 120); \
        msgList.add(TICK_, seq::MidiMsg(CONTROL, CHANNEL_, 98, LSB_), 120); \
        msgList.add(TICK_, seq::MidiMsg(CONTROL, CHANNEL_, 6 , DMSB_), 120); \
        msgList.add(TICK_, seq::MidiMsg(CONTROL, CHANNEL_, 38, DLSB_), 120);

        BYTE ch = 0;
        BYTE key = 37;
        BYTE value = 127;
        BYTE len1 = 3;
        BYTE len2 = 116;
        unsigned tick = 120; 
        BUILD_NRPN(ch, 0, 96, 0, 0, 0);      
        BUILD_NRPN(ch, 0, 96, 1, 0, 0);      
        BUILD_NRPN(ch, 0, 96, 2, 0, 0);      
        BUILD_NRPN(ch, 0, 83, 2, 0, 0);      
        BUILD_NRPN(ch, tick, 80, 0, 0, 0);      
        BUILD_NRPN(ch, tick, 80, 1, 15, 80);    
        BUILD_NRPN(ch, tick, 80, 2, key, 0);     
        BUILD_NRPN(ch, tick, 80, 3, value, 0);     
        BUILD_NRPN(ch, tick, 80, 4, len1, len2);    
        BUILD_NRPN(ch, tick, 80, 5, 3, 0);      
        BUILD_NRPN(ch, tick, 80, 12, 0, 0);     
        BUILD_NRPN(ch, tick, 80, 13, 1, 85);    
        BUILD_NRPN(ch, tick, 80, 14, 42, 0);    
        BUILD_NRPN(ch, tick, 80, 18, 1, 0);     
        BUILD_NRPN(ch, tick, 80, 19, 97, 0);    
        BUILD_NRPN(ch, tick, 80, 79, 127, 0);   
        BUILD_NRPN(ch, tick, 80, 80, 12, 0);    
        BUILD_NRPN(ch, tick, 80, 81, 8, 0);     
        BUILD_NRPN(ch, tick, 80, 82, 20, 0);    
        BUILD_NRPN(ch, tick, 80, 83, 28, 0);    
        BUILD_NRPN(ch, tick, 80, 84, 24, 0);    
        BUILD_NRPN(ch, tick, 80, 85, 10, 0);    
        BUILD_NRPN(ch, tick, 80, 86, 12, 0);    
        BUILD_NRPN(ch, tick, 80, 87, 12, 0);
        BUILD_NRPN(ch, tick, 80, 88, 0, 0);     
        BUILD_NRPN(ch, tick, 80, 89, 50, 0);    
        BUILD_NRPN(ch, tick, 80, 90, 50, 0);    
        BUILD_NRPN(ch, tick, 80, 127, 127, 0);  
        BUILD_NRPN(ch, 2*tick, 101, 2, 64, 0);    
        BUILD_NRPN(ch, 2*tick, 100, 2, 50, 0);    

#undef BUILD_NRPN
        host.setMidiMsgs((size_t)id, msgList);
        const RVstEvents& events = host.getPlugin((size_t)id).getEvents();
        cout << "[events] start" << endl;
        cout << events.toString() << endl;
        cout << "[events] end" << endl;
    } else {
        SequencerCore& core = seq::SequencerCore::instance();
        core.readProjectFile(fname, SMF1);
        Project& proj = core.getProject();
        shared_ptr<MidiMsgList> msgList(proj.getMidiMsgList());
        host.setMidiMsgs((size_t)id, *msgList);
    }
    WaveOutDevice d(65536, 4); 
    vector<WaveOutDeviceInfo> infos = d.getDeviceInfos();
    cout << "number of devices : (" << infos.size() << ")" << endl;
    d.selectDevice(0);
    d.setBufLimit(3);
    d.open(true);
    WaveBuf outBuf;
//    cout << "before process" << endl;
//    while (host.process((size_t)id, outBuf)) {
    while (host.process(outBuf)) {
//        cout << "process[" << id << "]" << endl;
        while (d.isFullBuf()) ::Sleep(100);
        d.send(outBuf);
    }
    d.waitPlayEnd();
    d.close();

    return 0;
}
